/**
 * 专门负责储存所有的物理页，和物理页页号与物理页地址间的相互转换。
 */
#include <mm/early_alloc.h>
#include <mm/pages.h>
#include <mm/compound_pages.h>
#include <mm/page_flags.h>
#include <cds/string.h>

static struct page_struct *pages = NULL;    /// 储存所有内存页结构体的数组。
static unsigned long start_pfn = 0; /// 整个内存的起始页帧号。
static unsigned long spanned_pages = 0; /// 内存页结构体数组的大小。
static unsigned long valid_pages = 0;  /// 范围内有效内存页的数量。
static unsigned long __kernel_offset = 0;   /// 恒等映射区中物理地址到虚拟地址的偏移。
static unsigned long __page_mask = 0;   /// 进行内存页对齐的mask。


int pages_init(
    unsigned long physical_start,
    unsigned long physical_end,
    unsigned long kernel_offset
)
{
    /// 此处为了方便后面的计算，让页帧号的范围对 2^PAGE_MAX_ORDER对齐。
    unsigned long order_mask = 1UL << (PAGE_SHIFT + PAGE_MAX_ORDER);
    order_mask -= 1;
    __page_mask = 1UL << PAGE_SHIFT;
    __page_mask -= 1;
    unsigned long physical_start_aligned = physical_start & (~order_mask);
    unsigned long physical_end_aligned =    \
        (physical_end + order_mask) & (~order_mask);
    if (physical_end_aligned <= physical_start_aligned) {
        /// 参数位置冲突。
        return -1;
    }
    if (kernel_offset & __page_mask) {
        /// 地址没有按内存页对齐。
        return -1;
    }

    /// 计算总共需要多少内存页结构体。因为上面已经进行对齐·操作，所以不用再次对齐
    unsigned long spanned = physical_end_aligned - physical_start_aligned;
    spanned >>= PAGE_SHIFT;
    /// 计算 pages 数组总共需要多大的内存。
    size_t size_of_pages = spanned * sizeof(struct page_struct);
    unsigned long pages_needed = (size_of_pages + __page_mask) >> PAGE_SHIFT;
    struct page_struct *__pages = early_pages_alloc_by_num(pages_needed);
    /// 分配失败。
    if (__pages == NULL) {
        return -1;
    }
    memset(__pages, 0, size_of_pages);

    /// 保存值，并退出。
    pages = __pages;
    start_pfn = physical_start_aligned >> PAGE_SHIFT;
    spanned_pages = spanned;
    __kernel_offset = kernel_offset;
    return 0;
}

struct page_struct *get_first_page(void)
{
    if (spanned_pages == 0) {
        return NULL;
    }
    return pages;
}

struct page_struct *get_last_page(void)
{
    if (spanned_pages == 0) {
        return NULL;
    }
    return pages + (spanned_pages - 1);
}

struct page_struct *physical_to_page(unsigned long physical)
{
    unsigned long pfn = physical_to_pfn(physical);
    /// 越界检测。
    if (pfn < start_pfn || pfn >= spanned_pages + start_pfn) {
        return NULL;
    }
    pfn -= start_pfn;
    /// 不返回无效的页。
    if (!is_page_valid(pages + pfn)) {
        return NULL;
    }
    return pages + pfn; 
}

unsigned long get_page_physical(struct page_struct *page)
{
    if (!is_page_valid(page)) {
        /// 内存页结构体无效的话就没有对应的物理地址。
        return 0;
    }
    return page->physical;
}

int is_page_valid(struct page_struct *page)
{
    if (page == NULL) {
        return 0;
    }
    return PageValid(page);
}

int set_pages_valid(
    unsigned long physical_start,
    unsigned long physical_end
)
{
    physical_start += __page_mask;
    physical_start &= ~__page_mask;
    unsigned long page_start = physical_start >> PAGE_SHIFT;
    unsigned long page_end = physical_end >> PAGE_SHIFT;

    /// 越界判断
    if (page_start >= page_end || 
        page_start < start_pfn || 
        page_end > start_pfn + spanned_pages
    ) {
        return -1;
    }

    page_start -= start_pfn;
    page_end -= start_pfn;
    for (unsigned long i = page_start; i < page_end; i++) {
        if (page_struct_init(
                pages + i, 
                physical_start + (i << PAGE_SHIFT)
            ) == NULL
        ) {
            return -1;
        }
        SetPageValid(pages + i);
        /// 默认所有的空闲页都属于伙伴系统，而前期分配器会使被分配的页不属于伙伴系统。
        SetPageBuddy(pages + i);
        valid_pages++;
    }

    return 0;
}

unsigned long get_valig_pages_num(void)
{
    return valid_pages;
}

unsigned long get_start_pfn(void)
{
    return start_pfn;
}

unsigned long get_spanned_pages_num(void)
{
    return spanned_pages;
}

struct page_struct *page_struct_init(
    struct page_struct *page,
    unsigned long physical
)
{
    if (page == NULL || (physical & __page_mask)) {
        return NULL;
    }

    page->flags = 0;
    page->physical = physical;
    atomic_init(&page->__map_count, 0);
    atomic_init(&page->__ref_count, 0);
    return page;
}

void lock_page_struct(struct page_struct *page)
{
    if (!is_page_valid(page)) {
        return;
    }
    SetPageLocked(page);
}

void unlock_page_struct(struct page_struct *page)
{
    if (!is_page_valid(page)) {
        return;
    }
    ClearPageLocked(page);
}

int page_struct_is_locked(struct page_struct *page)
{
    if (!is_page_valid(page)) {
        return 1;
    }
    return PageLocked(page);
}


/** 
 * @brief 判断一个页是否可被分割或合并。 
 * 
 * @retval true 可被分割或合并。 
 * @retval false 不可被分割或合并。 
 */
static inline int __is_page_order_changeable(struct page_struct *page)
{
    if (!is_page_valid(page)) {
        return 0;
    }
    if (PageCompound(page)) {
        return PageCompoundHead(page);
    } else {
        return 1;
    }
}

struct page_struct *create_compound_page(
    struct page_struct *page,
    unsigned long order
)
{
    /// 先检查 order 的合法性和范围内所有内存页的有效性。
    if (order == 0 || order >= PAGE_MAX_ORDER) {
        return NULL;
    }
    if (!__is_page_order_changeable(page) || PageCompound(page)) {
        return NULL;
    }
    /// 复合页必须按照 order 对齐。
    if (((page->physical >> PAGE_SHIFT) & ((1UL << order) - 1)) != 0) {
        return NULL;
    }
    /// 检查其余的页。
    for (int i = 1; i < (1UL << order); i++) {
        if (!__is_page_order_changeable(page + i) || PageCompound(page + i)) {
            return NULL;
        }
        if (page[i].zone != page->zone) {
            return NULL;
        }
    }

    SetPageCompound(page);
    SetPageCompoundHead(page);
    page->order = order;
    for (int i = 1; i < (1UL << order); i++) {
        SetPageCompound(page + i);
        page[i].header_page = page;
    }
    return page;
}

struct page_struct *__get_compound_buddy(
    struct page_struct *page, 
    unsigned long order
)
{
    unsigned long page_pfn = page->physical >> PAGE_SHIFT;
    unsigned long buddy_pfn = __get_compound_buddy_pfn(page_pfn, order);
    return page + (buddy_pfn - page_pfn);
}

struct page_struct *split_compound_page(struct page_struct *page)
{
    if (!__is_page_order_changeable(page) || !PageCompound(page)) {
        return 0;
    }
    unsigned long new_order = page->order - 1;
    struct page_struct *buddy = page + (1UL << new_order);

    /// 分割后大小的阶数为0，则不能再作为一个组合页。
    if (new_order == 0) {
        ClearPageCompoundHead(page);
        ClearPageCompound(page);
        page->order = 0;
        ClearPageCompound(buddy);
        buddy->order = 0;
        return buddy;
    }

    /// 更新组合页相关信息。
    for (int i = 1; i < (1UL << new_order); i++) {
        buddy[i].header_page = buddy;
    }
    SetPageCompoundHead(buddy);
    page->order = new_order;
    buddy->order = new_order;
    return buddy;
}

struct page_struct *merge_pages(
    struct page_struct *page1, 
    struct page_struct *page2
)
{
    if (!are_pages_mergeable(page1, page2)) {
        return NULL;
    }

    struct page_struct *header;
    struct page_struct *tail;
    if (page1->physical > page2->physical) {
        header = page2;
        tail = page1;
    } else {
        header = page1;
        tail = page2;
    }

    /// 对于两个单页，需要将两者设置为复合页。
    unsigned long old_order = header->order;
    unsigned long new_order = old_order + 1;
    if (new_order == 1) {
        tail->header_page = header;
        SetPageCompoundHead(header);
        SetPageCompound(header);
        SetPageCompound(tail);
        header->order = new_order;
        return header;
    }

    /// 更新组合页相关信息。
    ClearPageCompoundHead(tail);
    for (int i = 0; i < (1UL << old_order); i++) {
        tail[i].header_page = header;
    }
    header->order = new_order;

    return header;
}

unsigned long break_cmpound_page(struct page_struct *page)
{
    if (!__is_page_order_changeable(page) || !PageCompound(page)) {
        return 0;
    }
    unsigned long order_size = 1UL << page->order;
    for (int i = 0; i < order_size; i++) {
        ClearPageCompound(page + i);
        page[i].order = 0;
    }
    ClearPageCompoundHead(page);
    return order_size;
}

int are_pages_mergeable(
    struct page_struct *page1, 
    struct page_struct *page2
)
{
    if (!__is_page_order_changeable(page1) ||
        !__is_page_order_changeable(page2)
    ) {
        return 0;
    }
    if (page1->zone != page2->zone || page1->zone == NULL) {
        return 0;
    }
    if (page1->order != page2->order) {
        return 0;
    }
    if (page1->order >= (PAGE_MAX_ORDER - 1)) {
        return 0;
    }
    unsigned long page1_pfn = page1->physical >> PAGE_SHIFT;
    unsigned long page2_pfn = page2->physical >> PAGE_SHIFT;
    unsigned long order_mask = (1UL << page1->order) - 1;
    if ((page1_pfn & order_mask) != 0 ||
        (page2_pfn & order_mask) != 0
    ) {
        return 0;
    }
    return __get_compound_buddy_pfn(page1_pfn, page1->order) == page2_pfn;
}

int is_compound_page(struct page_struct *page)
{
    if (!is_page_valid(page)) {
        return 0;
    }
    return PageCompound(page);
}

int is_page_header(struct page_struct *page)
{
    if (!is_compound_page(page)) {
        return 0;
    }
    return PageCompoundHead(page);
}

struct page_struct *get_header_page(struct page_struct *page)
{
    if (!is_compound_page(page)) {
        return NULL;
    }
    if (PageCompoundHead(page)) {
        return page;
    }
    return page->header_page;
}

unsigned long get_page_order(struct page_struct *page)
{
    if (!is_page_header(page)) {
        return 0;
    }
    return page->order;
}

struct page_struct *get_first_compound_page(struct zone_struct *zone)
{
    if (zone == NULL) {
        return NULL;
    }
    if (zone->owned_pages == 0) {
        return NULL;
    }
    return physical_to_page(zone->zone_start_pfn << PAGE_SHIFT);
}

struct page_struct *get_next_compound_page(struct page_struct *page)
{
    unsigned long page_physical = get_page_physical(page);
    if (page_physical == 0) {
        return NULL;
    }
    unsigned long page_order = get_page_order(page);
    struct page_struct *next_page = \
        physical_to_page(page_physical + (1UL << (PAGE_SHIFT + page_order)));
    if (next_page == NULL || next_page->zone != page->zone) {
        return NULL;
    }
    return next_page;
}
